﻿using LPLS.DBEntities.LongGIModule;
using LPLS.DBInterface.LongGiMoudle;
using LPLS.FileWatchCommon;
using Masuit.Tools.Logging;
using Microsoft.Extensions.DependencyInjection;
using NetTaste;
using System.Data;
using System.Reflection;
using System.Text.Json;
using System.Timers;
using Topshelf;
namespace LongGi.WatchFilesChangeWinserver
{
    internal class Program
    {

        public static ServiceProvider ServiceProvider;
        static void Main(string[] args)
        {

          


            Console.WriteLine("Hello, World!");
            ///加载数据库IOC  一会需要写这些 POCO以及相关的
            ConfigureServices();

          //  var  Tubes=  GetTubesFiles(FilePath).Split(";");

         
            var rc = HostFactory.Run(x =>                         //1.启动程序
            {

                x.Service<TownCrier>(s =>                         //2.设置服务类型
                {
                    s.ConstructUsing(name => new TownCrier());    //3.创建服务实例
                    s.WhenStarted(tc => tc.Start());              //4.启动程序
                    s.WhenStopped(tc => tc.Stop());               //5.停止程序
                });
                x.RunAsLocalSystem();                             //6.本地系统运行

                x.SetDescription("LPLS数据采集服务");         //7.windows服务的描述
                x.SetDisplayName("LPLS数据采集服务");                        //8.windows服务的显示名称
                x.SetServiceName("LPLS.DataCollectionService");                        //9.windows服务的服务名称
            });

            var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode());  //11.退出程序
            Environment.ExitCode = exitCode;


            Console.ReadKey();
            Console.WriteLine("Hello, World!");

        }
        /// <summary>
        ///  找到相关 数据 
        /// </summary>
        /// <param name="obj"></param>
        /// <exception cref="NotImplementedException"></exception>
      
        private static void ConfigureServices()
        {
            var services = new ServiceCollection();
            LPLS. DBServices.DatabaseConfig.InitDataBaseConfig();
            services.AddSingleton<LPLS.DBEntities.DBContext>(sp => new LPLS.DBEntities.DBContext(LPLS.DBServices.DatabaseConfig.ConfiguredDbContexts));
            // 使用反射自动注册服务
            var serviceTypes = Assembly.Load("LPLS.DBServices").GetTypes()
                .Where(t => t.IsClass && t.GetInterfaces().Any(i => i.Name == "I" + t.Name));
            foreach (var serviceType in serviceTypes)
            {
                var interfaceType = serviceType.GetInterfaces().First(i => i.Name == "I" + serviceType.Name);
                services.AddSingleton(interfaceType, serviceType);
            }
            services.AddSingleton<LouLvOP>();
            services.AddSingleton<EXCELOP>();
            services.AddSingleton<KaJiangOP>();         
            ServiceProvider = services.BuildServiceProvider();
        }

     
        /// <summary>
        /// 获取文件夹下的文件 是炉管
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private static string GetTubesFiles(string path)
        {
            string folderPath = path;

            try
            {
                // 获取所有子文件夹的完整路径
                string[] allDirectories = Directory.GetDirectories(folderPath);

                // 提取文件夹名称
                var folderNames = allDirectories.Select(path => Path.GetFileName(path));

                // 输出结果
                foreach (string folderName in folderNames)
                {
                    Console.WriteLine(folderName);
                }

                // 或者直接转换为数组
                string[] folderNamesArray = folderNames.ToArray();
                string semicolonSeparated = string.Join(";", folderNamesArray);

                return semicolonSeparated;
            }
            catch (Exception ex)
            {
                return "";
                Console.WriteLine($"错误: {ex.Message}");
            }





        }


    }
    public class TownCrier
    {

        static System.Timers.Timer timer1 = new System.Timers.Timer();


        /// <summary>
        /// 监听的数据文件
        /// </summary>
        public static string FilePath = System.Configuration.ConfigurationManager.AppSettings["Path"];
        public static ServiceProvider ServiceProvider;

        public TownCrier()
        {
            ServiceProvider = Program.ServiceProvider;
        }
        public void Start()
        {
            timer1.Enabled = true;
            BackgroundWork();
          
        }
        public void Stop()
        {
            timer1.Enabled = false;
            StopWork();
        }

        public static void BackgroundWork()
        {

            timer1.Interval = 1000*60;
            // 设置 Elapsed 事件处理程序
            timer1.Elapsed += OnTime1Event;

            // 设置 Timer 为自动重置
            timer1.AutoReset = true;

            // 启动 Timer
            timer1.Enabled = true;
            FileWatchEvent.OnExcelFileCreatedEvent += FileWatchEvent_OnExcelFileCreatedEvent;
            StartExcelWatchSample(FilePath);
        }

        private static void OnTime1Event(object? sender, ElapsedEventArgs e)
        {

            if (ExcelReader.ErrorPaths.Count==0)
            {
                return;
            }
            var path = ExcelReader.ErrorPaths.FirstOrDefault();      
            var exop = ServiceProvider.GetService<EXCELOP>();
            exop.ReadExcel(path);


        }

        public static  void  StopWork()
        {
            FileWatchEvent.OnExcelFileCreatedEvent -= FileWatchEvent_OnExcelFileCreatedEvent;
        }
        private static void FileWatchEvent_OnExcelFileCreatedEvent(string obj)
        {

           var exop=   ServiceProvider.GetService<EXCELOP>();
            exop.ReadExcel(obj);


        }
        public static void StartExcelWatchSample(string path)
        {
            // 监听D:\Test目录下及其子目录的Excel文件创建
            var watcher = ExcelFileWatcher.StartWatching(path, filePath =>
            {
                // 指定logs文件夹路径（可以是相对路径或绝对路径）
                string logsFolderPath = "logs"; // 当前目录下的logs文件夹
                                               // 检查并清理日志（10MB限制）
                LogCleaner.CleanLogsIfExceedsSize(logsFolderPath);
                //   FileWatchEvent.RaiseExcelFileCreated(filePath);
                // 这里处理新创建的Excel文件路径
                Console.WriteLine($"新Excel文件: {filePath}");
            });
        }
    }
    public class EXCELOP    
    {
    
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path">excel表的路径</param>
        public  void ReadExcel( string path) {



            try
            {
                ExcelReader excelReader = new ExcelReader();
                Thread.Sleep(1000);
                ///读取到了 datatable
                var data = excelReader.ReadExcelWithDynamicHeader(path);
                var s = path.Split('\\').Last();
                var ss = path.Split("\\");
                ///获取到了 炉管
                ///接下来要对数据进行分析整合然后入库 抄 隆基模块的业务逻辑
                DataTable dataTable = new DataTable();  //await reader.ReadExcelWithDynamicHeader(item);
                ExcelFileHeaderModel model = new ExcelFileHeaderModel(path, s, data);

                if (model.DT==null)
                {
                    return;
                }
                model.TubeName = s.Split(".")[0].Split("_")[0].ToString();
                model.Value = GetRateOfLeakageValue(model.DT);
                if (model.Value==-1)
                {
                    return;
                }
                ///增加漏率 
                var loulv = Program.ServiceProvider.GetService<LouLvOP>();
                loulv.InsertData(model);
                var kaJiang = Program.ServiceProvider.GetService<KaJiangOP>();
                kaJiang.InserttData(model);
               
            }
            catch (Exception ex)
            {
                LogManager.Info($"函数：{nameof(ReadExcel)} 报错了！！！！！！！！！！！！！！！！！！！");
                LogManager.Error(ex);
            }
           
        }


        private static  float GetRateOfLeakageValue(DataTable dt)
        {
            if (dt==null)
            {
                return -1;
            }
            try
            {
                var lastdata = dt.Rows[dt.Rows.Count - 1];

                var value = float.Parse(lastdata["漏率"].ToString());

                float rounded = (float)Math.Round(value, 2);
                return rounded;
            }
            catch (Exception ex)
            {
              //  LongGiEventCommon.PubshMessageEventHander("表中未找到 漏率  列 请检查！！！ ");

                return 9999;
            }


        }
    }
    public   class LouLvOP 
    {



        private ILoulvDataService DBservice;
        public LouLvOP(ILoulvDataService dbservice) {

            DBservice = dbservice;
        }

        public  void  InsertData(ExcelFileHeaderModel data ) 
        {
            LoulvData model = new LoulvData();
            model.Value=data.Value;
            model.TubeName = data.TubeName;
            model.ExcelName = data.ExcelName;
            model.TechnologyName = data.TechnologyName;
            model.Time = data.Time;
            DBservice.InsertData(model);
        }
    }
    /// <summary>
    /// 卡浆
    /// </summary>
    public class KaJiangOP 
    {

        private void GetCOnfigMess()
        {  // 获取生成目录下的 JSON 文件路径
            var baseDir = AppContext.BaseDirectory;
            var configPath = Path.Combine(baseDir, "LongGi.json");

            if (!File.Exists(configPath))
                throw new FileNotFoundException("配置文件未找到: " + configPath);

            // 读取并解析 JSON
            var json = File.ReadAllText(configPath);
            using var doc = JsonDocument.Parse(json);

            NiujuValueColName= doc.RootElement.GetProperty("NiujuValueColName").GetString();
            ZhouPointName= doc.RootElement.GetProperty("ZhouPointName").GetString();
            StepName= doc.RootElement.GetProperty("StepName").GetString();
        }
        private string NiujuValueColName, ZhouPointName, StepName;
        private IKaJiangDataService DBService;
        public KaJiangOP(IKaJiangDataService kaJiangDataService  ) {

            DBService=kaJiangDataService;
            GetCOnfigMess();
        }
        public  void InserttData(ExcelFileHeaderModel item )
        {
            var datatable = item.DT;      
            try
            {

          
            List<KaJiangData> li = new List<KaJiangData>();

                #region  进舟
                // 如果是数值类型，不需要引号
                var jinzhoudt = item.DT.Select($"{StepName} = '进舟'").CopyToDataTable();
                if (jinzhoudt.Rows.Count<=0)
                {
                    LogManager.Info($"函数{nameof (InserttData)} 类：{nameof(KaJiangOP)} 钟的 步名称为 进舟的列 没有解析到！！！！！！！！！！！！！！");

                    string clonsName = "表表单列名称为：";
                    for (int i = 0; i < jinzhoudt.Columns.Count; i++)
                    {
                        clonsName+= jinzhoudt.Columns[i].ColumnName + ",";
                    }
                    LogManager.Info(clonsName);
                    
                }
                //在此计算    进舟  出舟的
                //空载  负载
                //平均值  最大值 

                var (jinzhouFuzaiResult, jinzhouKongzaiResult) = SplitDataTableExcludingContinuousMaxFloatValue(jinzhoudt, ZhouPointName);
                //1  计算 平均值  
                // 使用示例
                /// 进舟负载         
                var (jinzhouFuzaimaxAbs, jinzhouFuzaisumAbs, jinzhouFuzaiavgAbs) = MyDataTableExtensions.GetColumnAbsStats(jinzhouFuzaiResult, NiujuValueColName);

            #region 进舟 负载 最大值
            KaJiangData jinzhouFuzaiMax = new KaJiangData();

            jinzhouFuzaiMax.Value =(float) Math.Round((double)jinzhouFuzaimaxAbs, 2);
            jinzhouFuzaiMax.StepName = "进舟";
            jinzhouFuzaiMax.Time = item.Time;
            jinzhouFuzaiMax.TubeName = item.TubeName;
            jinzhouFuzaiMax.TypeNumber = 1;
            jinzhouFuzaiMax.ExcelName = item.ExcelName;
            jinzhouFuzaiMax.TechnologyName = item.TechnologyName;
            jinzhouFuzaiMax.TypeValue = 1;
            li.Add(jinzhouFuzaiMax);
            #endregion
            #region 进舟 负载 平均值
            KaJiangData jinzhouFuzaiAgv = new KaJiangData();
            jinzhouFuzaiAgv.Value = (float)Math.Round((double)jinzhouFuzaiavgAbs, 2);
            jinzhouFuzaiAgv.StepName = "进舟";
            jinzhouFuzaiAgv.Time = item.Time;
            jinzhouFuzaiAgv.TubeName = item.TubeName;
            jinzhouFuzaiAgv.TypeNumber = 1;
            jinzhouFuzaiAgv.ExcelName = item.ExcelName;
            jinzhouFuzaiAgv.TechnologyName = item.TechnologyName;
            jinzhouFuzaiAgv.TypeValue = 0;
            li.Add(jinzhouFuzaiAgv);
            #endregion





            //进舟 空载
            var (jinzhouKongzaimaxAbs, jinzhouKongzaisumAbs, jinzhouKongzaiavgAbs) = MyDataTableExtensions.GetColumnAbsStats(jinzhouKongzaiResult, NiujuValueColName);
            #region  进舟 空载 最大值
            KaJiangData jinzhouKongzaiMax = new KaJiangData();
            jinzhouKongzaiMax.Value = (float)Math.Round((double)jinzhouKongzaimaxAbs, 2);
            jinzhouKongzaiMax.StepName = "进舟";
            jinzhouKongzaiMax.Time = item.Time;
            jinzhouKongzaiMax.TubeName = item.TubeName;
            jinzhouKongzaiMax.TypeNumber = 0;
            jinzhouKongzaiMax.ExcelName = item.ExcelName;
            jinzhouKongzaiMax.TechnologyName = item.TechnologyName;
            jinzhouKongzaiMax.TypeValue = 1;
            li.Add(jinzhouKongzaiMax);

            #endregion
            #region 进舟空载 平均值

            KaJiangData jinzhouKongzaiAgv = new KaJiangData();
            jinzhouKongzaiAgv.Value = (float)Math.Round((double)jinzhouKongzaiavgAbs, 2);
            jinzhouKongzaiAgv.StepName = "进舟";
            jinzhouKongzaiAgv.Time = item.Time;
            jinzhouKongzaiAgv.TubeName = item.TubeName;
            jinzhouKongzaiAgv.TypeNumber = 0;
            jinzhouKongzaiAgv.ExcelName = item.ExcelName;
            jinzhouKongzaiAgv.TechnologyName = item.TechnologyName;
            jinzhouKongzaiAgv.TypeValue = 0;
            li.Add(jinzhouKongzaiAgv);

            #endregion



            #endregion
                #region 出舟


            var chouzhoudt = item.DT.Select("步名称 = '出舟'").CopyToDataTable();

                if (chouzhoudt.Rows.Count <= 0)
                {
                    LogManager.Info($"函数{nameof(InserttData)} 类：{nameof(KaJiangOP)} 钟的 步名称为 出舟的列 没有解析到！！！！！！！！！！！！！！");

                }
                // 如果是数值类型，不需要引号

                //在此计算    进舟  出舟的
                //空载  负载
                //平均值  最大值 

                var (chouzhouFuzaiResult, chouzhouKongzaiResult) = SplitDataTableExcludingContinuousMaxFloatValue(chouzhoudt, ZhouPointName);




          
            ///        
            var (jchouzhouFuzaimaxAbs, chouzhouFuzaisumAbs, chouzhouFuzaiavgAbs) = MyDataTableExtensions.GetColumnAbsStats(chouzhouFuzaiResult, NiujuValueColName);

            #region 出舟 负载  最大值
            KaJiangData chuzhouFuzaiMax = new KaJiangData();
            chuzhouFuzaiMax.Value = (float)Math.Round((double)jchouzhouFuzaimaxAbs, 2);
            chuzhouFuzaiMax.StepName = "出舟";
            chuzhouFuzaiMax.Time = item.Time;
            chuzhouFuzaiMax.TubeName = item.TubeName;
            chuzhouFuzaiMax.TypeNumber = 1;
            chuzhouFuzaiMax.ExcelName = item.ExcelName;
            chuzhouFuzaiMax.TechnologyName = item.TechnologyName;
            chuzhouFuzaiMax.TypeValue = 1;
            li.Add(chuzhouFuzaiMax);
            #endregion

            #region 出舟  负载 平均值
            KaJiangData chuzhouFuzaiAgv = new KaJiangData();
            chuzhouFuzaiAgv.Value = (float)Math.Round((double)chouzhouFuzaiavgAbs, 2);
            chuzhouFuzaiAgv.StepName = "出舟";
            chuzhouFuzaiAgv.Time = item.Time;
            chuzhouFuzaiAgv.TubeName = item.TubeName;
            chuzhouFuzaiAgv.TypeNumber = 1;
            chuzhouFuzaiAgv.ExcelName = item.ExcelName;
            chuzhouFuzaiAgv.TechnologyName = item.TechnologyName;
            chuzhouFuzaiAgv.TypeValue = 0;
            li.Add(chuzhouFuzaiAgv);
            #endregion

         

                //出舟 空载
                var (chuzhouKongzaimaxAbs, chuzhouKongzaisumAbs, chuzhouKongzaiavgAbs) = MyDataTableExtensions.GetColumnAbsStats(chouzhouKongzaiResult, NiujuValueColName);

            #region 出舟  空载 最大值
            KaJiangData chuzhouKongzaiMax = new KaJiangData();
            chuzhouKongzaiMax.Value = (float)Math.Round((double)chuzhouKongzaimaxAbs, 2);
            chuzhouKongzaiMax.StepName = "出舟";
            chuzhouKongzaiMax.Time = item.Time;
            chuzhouKongzaiMax.TubeName = item.TubeName;
            chuzhouKongzaiMax.TypeNumber = 0;
            chuzhouKongzaiMax.ExcelName = item.ExcelName;
            chuzhouKongzaiMax.TechnologyName = item.TechnologyName;
            chuzhouKongzaiMax.TypeValue = 1;
            li.Add(chuzhouKongzaiMax);
            #endregion

            #region 出舟  空载 平均值
            KaJiangData chuzhouKongzaiAgv = new KaJiangData();
            chuzhouKongzaiAgv.Value = (float)Math.Round((double)chuzhouKongzaiavgAbs, 2);
            chuzhouKongzaiAgv.StepName = "出舟";
            chuzhouKongzaiAgv.Time = item.Time;
            chuzhouKongzaiAgv.TubeName = item.TubeName;
            chuzhouKongzaiAgv.TypeNumber = 0;
            chuzhouKongzaiAgv.ExcelName = item.ExcelName;
            chuzhouKongzaiAgv.TechnologyName = item.TechnologyName;
            chuzhouKongzaiAgv.TypeValue = 0;
            li.Add(chuzhouKongzaiAgv);
            #endregion



         






            #endregion
             var  res=    DBService.InsertData(li);

            if (res==li.Count)
            {
                LogManager.Info($"数据插入成功 表：{item.ExcelName}");

            }
            else
            {
                LogManager.Info($"数据插入失败 表：{item.ExcelName}");
            }
            }
            catch (Exception ex)
            {
                LogManager.Info($"数据插入失败  函数：{nameof(InserttData)}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                LogManager.Error(ex);
            }

        }
        #region  DataTable 操作 函数

        /// <summary>
        /// 根据指定float类型列的最大值将DataTable分裂为两个部分，最大值连续行不会出现在结果中
        /// 专门处理最大值连续出现的情况，将连续的多个最大值行作为一个整体排除
        /// 第一部分：最大值连续行之前的所有行（不包含最大值连续行）
        /// 第二部分：最大值连续行之后的所有行（不包含最大值连续行）
        /// </summary>
        /// <param name="sourceTable">源DataTable，需要分裂的原始数据表</param>
        /// <param name="columnName">用于查找最大值的列名，该列的数据类型应为float</param>
        /// <param name="tolerance">浮点数比较的容差，用于解决浮点数精度问题，默认值为0.0001f</param>
        /// <returns>包含两个DataTable的元组：beforeTable(最大值连续行之前的行)和afterTable(最大值连续行之后的行)</returns>
        /// <exception cref="ArgumentNullException">当sourceTable为null时抛出</exception>
        /// <exception cref="ArgumentException">当列名为空或不存在，或列类型不是float时抛出</exception>
        /// <exception cref="InvalidOperationException">当数据表中没有行时抛出</exception>
        public static (DataTable beforeTable, DataTable afterTable) SplitDataTableExcludingContinuousMaxFloatValue(
            DataTable sourceTable, string columnName, float tolerance = 0.0001f)
        {
            // 参数验证
            if (sourceTable == null)
                throw new ArgumentNullException(nameof(sourceTable), "源数据表不能为null");

            if (string.IsNullOrEmpty(columnName))
                throw new ArgumentException("列名不能为空", nameof(columnName));

            if (!sourceTable.Columns.Contains(columnName))
                throw new ArgumentException($"数据表中不存在列 '{columnName}'", nameof(columnName));

            ////验证列类型是否为float，确保类型安全
            ////if (sourceTable.Columns[columnName].DataType != typeof(float))
            ////    throw new ArgumentException($"列 '{columnName}' 的类型不是 float", nameof(columnName));

            // 检查数据表是否有行
            if (sourceTable.Rows.Count == 0)
                throw new InvalidOperationException("数据表中没有数据行");

            // 创建两个新的DataTable，结构与原表相同（克隆表结构）
            DataTable beforeTable = sourceTable.Clone();
            DataTable afterTable = sourceTable.Clone();

            // 查找指定列的最大值
            float maxValue = FindMaxFloatValue(sourceTable, columnName);

            // 查找最大值连续行的起始和结束索引
            var (startIndex, endIndex) = FindContinuousMaxValueRange(sourceTable, columnName, maxValue, tolerance);

            // 遍历源数据表的所有行，根据最大值连续行的位置进行分裂
            for (int i = 0; i < sourceTable.Rows.Count; i++)
            {
                DataRow row = sourceTable.Rows[i];

                // 如果当前行在最大值连续行之前，添加到beforeTable
                if (i < startIndex)
                {
                    beforeTable.ImportRow(row);
                }
                // 如果当前行在最大值连续行之后，添加到afterTable
                else if (i > endIndex)
                {
                    afterTable.ImportRow(row);
                }
                // 如果当前行在最大值连续行范围内，跳过（不添加到任何表）
            }

            // 返回分裂后的两个DataTable（都不包含最大值连续行）
            return (beforeTable, afterTable);
        }

        /// <summary>
        /// 查找最大值连续行的起始和结束索引
        /// 处理最大值连续出现的情况，找到连续的多个最大值行
        /// </summary>
        /// <param name="sourceTable">源DataTable</param>
        /// <param name="columnName">列名</param>
        /// <param name="maxValue">最大值</param>
        /// <param name="tolerance">浮点数比较的容差</param>
        /// <returns>包含起始索引和结束索引的元组</returns>
        private static (int startIndex, int endIndex) FindContinuousMaxValueRange(
            DataTable sourceTable, string columnName, float maxValue, float tolerance)
        {
            int startIndex = -1;
            int endIndex = -1;
            bool inMaxRange = false;

            // 遍历所有行，查找最大值连续范围
            for (int i = 0; i < sourceTable.Rows.Count; i++)
            {
                DataRow row = sourceTable.Rows[i];

                // 跳过空值行
                if (row.IsNull(columnName))
                    continue;

                float currentValue = float.Parse(row[columnName].ToString());
                bool isMaxValue = Math.Abs(currentValue - maxValue) <= tolerance;

                // 如果当前行是最大值行
                if (isMaxValue)
                {
                    // 如果不在最大值范围内，开始一个新的范围
                    if (!inMaxRange)
                    {
                        inMaxRange = true;
                        startIndex = i;
                        endIndex = i;
                    }
                    else
                    {
                        // 如果在最大值范围内，更新结束索引
                        endIndex = i;
                    }
                }
                else
                {
                    // 如果当前行不是最大值行，且之前在最大值范围内，则结束范围查找
                    if (inMaxRange)
                        break;
                }
            }

            // 如果没有找到最大值行，抛出异常
            if (startIndex == -1)
                throw new InvalidOperationException($"在列 '{columnName}' 中未找到最大值 {maxValue}");

            return (startIndex, endIndex);
        }

        /// <summary>
        /// 在DataTable的指定float列中查找最大值
        /// </summary>
        /// <param name="sourceTable">源DataTable</param>
        /// <param name="columnName">列名</param>
        /// <returns>指定列中的最大值</returns>
        private static float FindMaxFloatValue(DataTable sourceTable, string columnName)
        {
            // 初始化最大值为float的最小可能值
            float maxValue = float.MinValue;
            bool hasValue = false;

            // 遍历所有行，查找最大值
            foreach (DataRow row in sourceTable.Rows)
            {
                // 跳过空值行
                if (row.IsNull(columnName))
                    continue;

                float currentValue = float.Parse(row[columnName].ToString());

                // 如果当前值大于已知最大值，更新最大值
                if (!hasValue || currentValue > maxValue)
                {
                    maxValue = currentValue;
                    hasValue = true;
                }
            }

            // 如果没有找到有效值（所有行都是空值），抛出异常
            if (!hasValue)
                throw new InvalidOperationException($"列 '{columnName}' 中没有有效的数值");

            return maxValue;
        }

        /// <summary>
        /// 获取包含最大值连续行的单独DataTable（用于调试或特殊需求）
        /// </summary>
        /// <param name="sourceTable">源DataTable</param>
        /// <param name="columnName">列名</param>
        /// <param name="tolerance">浮点数比较的容差</param>
        /// <returns>包含所有最大值连续行的DataTable</returns>
        public static DataTable GetContinuousMaxValueRows(DataTable sourceTable, string columnName, float tolerance = 0.0001f)
        {
            // 参数验证
            if (sourceTable == null)
                throw new ArgumentNullException(nameof(sourceTable));

            if (string.IsNullOrEmpty(columnName))
                throw new ArgumentException("列名不能为空");

            if (!sourceTable.Columns.Contains(columnName))
                throw new ArgumentException($"数据表中不存在列 '{columnName}'");

            if (sourceTable.Columns[columnName].DataType != typeof(float))
                throw new ArgumentException($"列 '{columnName}' 的类型不是 float");

            // 查找最大值
            float maxValue = FindMaxFloatValue(sourceTable, columnName);

            // 查找最大值连续范围
            var (startIndex, endIndex) = FindContinuousMaxValueRange(sourceTable, columnName, maxValue, tolerance);

            // 创建结果表
            DataTable maxValueTable = sourceTable.Clone();

            // 添加最大值连续行
            for (int i = startIndex; i <= endIndex; i++)
            {
                maxValueTable.ImportRow(sourceTable.Rows[i]);
            }

            return maxValueTable;
        }

        #endregion

    }
    public static class MyDataTableExtensions
    {
        public static decimal GetColumnAbsMax(DataTable dt, string columnName)
        {
            return dt.AsEnumerable()
                    .Where(row => !row.IsNull(columnName))
                    .Max(row => Math.Abs(Convert.ToDecimal(row[columnName])));
        }

        public static decimal GetColumnAbsSum(DataTable dt, string columnName)
        {
            return dt.AsEnumerable()
                    .Where(row => !row.IsNull(columnName))
                    .Sum(row => Math.Abs(Convert.ToDecimal(row[columnName])));
        }

        public static decimal GetColumnAbsAverage(DataTable dt, string columnName)
        {
            return dt.AsEnumerable()
                    .Where(row => !row.IsNull(columnName))
                    .Average(row => Math.Abs(Convert.ToDecimal(row[columnName])));
        }

        // 同时获取所有统计信息
        public static (decimal max, decimal sum, decimal avg) GetColumnAbsStats(DataTable dt, string columnName)
        {
            var validRows = dt.AsEnumerable()
            
                             .Where(row => !row.IsNull(columnName))
                             .Select(row => Math.Abs(Convert.ToDecimal(row[columnName]))).Order()
                             .ToList();

            if (!validRows.Any())
                return (0, 0, 0);

            return (validRows.Max(), validRows.Sum(), validRows.Average());
        }
    }
}
